from Storage import Storage
from bettybox.Document import Document
from bettybox.PhysicalLocation import PhysicalLocation

import thrift.transport.THttpClient as THttpClient
import thrift.protocol.TBinaryProtocol as TBinaryProtocol
import evernote.edam.userstore.UserStore as UserStore
import evernote.edam.userstore.constants as UserStoreConstants
import evernote.edam.notestore.NoteStore as NoteStore
import evernote.edam.error.ttypes as Errors
import evernote.edam.type.ttypes as Types
import hashlib, binascii

class EvernoteStorage(Storage):
  
  def __init__(self, server, user, password):
    self.__server = server
    self.__user = user
    self.__password = password
  
  def Connect(self):
    consumerKey = "fournierg"
    consumerSecret = "931009ab23471739"
    userStoreUri = "https://" + self.__server + "/edam/user"
    noteStoreUriBase = "https://" + self.__server + "/edam/note/"
    
    userStoreHttpClient = THttpClient.THttpClient(userStoreUri)
    userStoreProtocol = TBinaryProtocol.TBinaryProtocol(userStoreHttpClient)
    userStore = UserStore.Client(userStoreProtocol)
    
    versionOK = userStore.checkVersion("Python EDAMTest",
                                       UserStoreConstants.EDAM_VERSION_MAJOR,
                                       UserStoreConstants.EDAM_VERSION_MINOR)
    
    if not versionOK:
      raise Exception('The EDAM protocol of Evernote is not up to date.')
  
    try :
      authResult = userStore.authenticate(self.__user, self.__password,
                                        consumerKey, consumerSecret)
    except Errors.EDAMUserException as e:
      # See http://www.evernote.com/about/developer/api/ref/UserStore.html#Fn_UserStore_authenticate
      parameter = e.parameter
      errorCode = e.errorCode
      errorText = Errors.EDAMErrorCode._VALUES_TO_NAMES[errorCode]
      
      print "Authentication failed (parameter: " + parameter + " errorCode: " + errorText + ")"
      
      if errorCode == Errors.EDAMErrorCode.INVALID_AUTH:
        if parameter == "consumerKey":
          if consumerKey == "en-edamtest":
            print "You must replace the variables consumerKey and consumerSecret with the values you received from Evernote."
          else:
            print "Your consumer key was not accepted by", self.__server
            print "This sample client application requires a client API key. If you requested a web service API key, you must authenticate using OAuth."
          print "If you do not have an API Key from Evernote, you can request one from http://www.evernote.com/about/developer/api"
        elif parameter == "username":
          print "You must authenticate using a username and password from", self.__server
          if self.__server != "www.evernote.com":
            print "Note that your production Evernote account will not work on", self.__server
            print "You must register for a separate test account at https://" + self.__server + "/Registration.action"
        elif parameter == "password":
          print "The password that you entered is incorrect"
          
      raise Exception("Authentication failed, exit.")
      
    user = authResult.user
    self.__authToken = authResult.authenticationToken
    print "Authentication was successful for " + user.username
    print "Authentication token = " + self.__authToken
    
    noteStoreUri =  noteStoreUriBase + user.shardId
    noteStoreHttpClient = THttpClient.THttpClient(noteStoreUri)
    noteStoreProtocol = TBinaryProtocol.TBinaryProtocol(noteStoreHttpClient)
    self.__noteStore = NoteStore.Client(noteStoreProtocol)
  

  def Store(self, document):
    
    note = Types.Note()
    note.title = document.title
    note.content = '<?xml version="1.0" encoding="UTF-8"?>'
    note.content += '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'
    note.content += '<en-note>'
    note.content += document.content
    if not document.physicalLocation is None:
      note.content += '<br/>'
      note.content = 'This document is located in the box number %d, section %d.' % (document.physicalLocation.BoxNumber, document.physicalLocation.SectionNumber)
    note.resources = []
    
    if not document.attachment is None:
      resource = open(document.attachment, 'rb').read()
      self.__writeResource(resource, note, 'application/pdf')
      
    if len(document.tags) > 0:
      note.tagNames = document.tags
      
    #if len(document.wordIndex) > 0:
    #  resource = " ".join(document.wordIndex)
    #  self.__writeResource(resource, note, 'text/plain')
    
    note.content += '</en-note>'
    
    createdNote = self.__noteStore.createNote(self.__authToken, note)

    print "Successfully created a new note with GUID: ", createdNote.guid, "title: ", note.title
  
  def Contains(self, docTitle):
    query = NoteStore.NoteFilter()
    query.words = 'intitle:"' + docTitle + '"'

    resultSpec = NoteStore.NotesMetadataResultSpec()
    resultSpec.includeTitle = True
    result = self.__noteStore.findNotesMetadata(self.__authToken, query, 0, 999, resultSpec)

    for note in result.notes:
      if note.title == docTitle:
        return True

    return False

  # return a note from its guid
  def GetNote(self, guid, func=None):
    note = self.__noteStore.getNote(self.__authToken, guid, True, True, True, True)
    if func is None:
      return note
    else:
      return func(note)

  # return the GUID of an existing note
  def FindNote(self, queryText):
    query = NoteStore.NoteFilter()
    query.words = queryText

    try:
      result = self.__noteStore.findNotes(self.__authToken, query, 0, 1)
      if len(result.notes) == 0:
        raise Exception("No notes found", query)
      else:
        return result.notes[0].guid
    except Errors.EDAMNotFoundException, e:
      raise Exception("No notes found", e)

  # update a note
  def UpdateNote(self, note):
    self.__updateNote(self.__authToken, note)

  def GetPhysicalLocation(self):
    guid = self.FindNote('intitle:"BettyBoxCurrentPhysicalLocation"')
    return self.GetNote(guid, lambda x: PhysicalLocation.FromText(x.content))

  def WritePhysicalLocation(self, location):
    guid = None
    try:
      guid = self.FindNote('intitle:"BettyBoxCurrentPhysicalLocation"')
    except:
      print "Physical location not found, create a new one"
      guid = None

    note = Types.Note()
    note.title = 'BettyBoxCurrentPhysicalLocation'
    note.content = '<?xml version="1.0" encoding="UTF-8"?>'
    note.content += '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'
    note.content += '<en-note>'
    note.content += location.ToText()
    note.content += '</en-note>'

    if guid is None:
      createdNote = self.__noteStore.createNote(self.__authToken, note)
      print "Successfully created a new note with GUID: ", createdNote.guid, "title: ", note.title
    else:
      note.guid = guid
      self.__noteStore.updateNote(self.__authToken, note)
      print "Successfully updated existing note: ", note.guid, "title: ", note.title

    
  def __writeResource(self, resource, note, mimeType):
      md5 = hashlib.md5()
      md5.update(resource)
      hsh = md5.digest()
      hashHex = binascii.hexlify(hsh)

      data = Types.Data()
      data.size = len(resource)
      data.bodyHash = hsh
      data.body = resource
      
      noteResource = Types.Resource()
      noteResource.mime = mimeType
      noteResource.data = data
      
      note.content += '<en-media type="' + mimeType + '" hash="' + hashHex + '"/>'
      note.resources += [noteResource]
      
      
if __name__ == '__main__':
  storage = EvernoteStorage('sandbox.evernote.com', 'fournierg', 'xxxxxx')
  storage.Connect()
  document = Document()
  document.title = 'test'
  document.content = 'this is my test'
  #storage.Store(document)

  loc = PhysicalLocation(1, 1)
  storage.WritePhysicalLocation(loc)
  loc = storage.GetPhysicalLocation()
  print loc.ToText()

